/*************************************************************
  Cocus Core - A simple PIC16 core emulator for PIC18
  
  version 2.0 by Santiago H. (santiagohssl@gmail.com)

  UNDER GNU LGPL

  file: service_usart.c
*************************************************************/

#include <p18cxxx.h>
#include "vmcore.h"
#include "service_usart.h"

/* *******************************************************************************
	USART Service
 ******************************************************************************* */

void vm_services_usart_init(void *dev)
{
	vm_writeSingleBit(dev, CORE_USART_PIR1, CORE_USART_BIT_TXIF, 1);
	vm_writeSingleBit(dev, CORE_USART_PIR1, CORE_USART_BIT_RCIF, 0);
	
	vm_writeRam(dev, CORE_USART_TXSTA, CORE_USART_DEFAULT_TXSTA);
	vm_writeRam(dev, CORE_USART_RCSTA, CORE_USART_DEFAULT_RCSTA);	
	
	vm_writeRam(dev, CORE_USART_TXREG, 0x00);
	vm_writeRam(dev, CORE_USART_RCREG, 0x00);
}

void vm_services_usart(void *dev, vm_opcode *opcode)
{
	if (vm_readSingleBit(dev, CORE_USART_TXSTA, CORE_USART_BIT_TRMT) == 0)
	{
		/* byte sent correctly */
		vm_writeSingleBit(dev, CORE_USART_TXSTA, CORE_USART_BIT_TRMT, 1);	
		
		// ok, raise a user event
		vm_services_usart_callback_tx(dev, vm_readRam(dev, CORE_USART_TXREG));
		
		/* set TXIF interrupt flag */
		vm_writeSingleBit(dev, CORE_USART_PIR1, CORE_USART_BIT_TXIF, 1);	
		
		/* if TXIF interrupt is enabled, raise it */
		if (vm_readSingleBit(dev, CORE_USART_PIE1, CORE_USART_BIT_TXIE))
			vm_raiseInt(dev);
	}
}

char vm_services_usart_read_hook(void *dev, int pos, char * data)
{
	/* check if memory destination is RCREG */
	if (pos == CORE_USART_RCREG)
		/* if it is, check if USART is enabled, and continuous receive is enabled */
		if (vm_readSingleBit(dev, CORE_USART_RCSTA, CORE_USART_BIT_SPEN) &&
			vm_readSingleBit(dev, CORE_USART_RCSTA, CORE_USART_BIT_CREN))
		{	
			/* clear the receive buffer */
			vm_writeRam(dev, CORE_USART_RCREG, 0x00); 
			
			/* clear the receive interrupt flag */
			vm_writeSingleBit(dev, CORE_USART_PIR1, CORE_USART_BIT_RCIF, 0);

		}	
	
	return 0;
}

char vm_services_usart_write_hook(void *dev, int pos, char * data)
{
	/* check if memory destination is TXREG */
	if (pos == CORE_USART_TXREG)
		/* if it is, check if USART enabled, and TX enabled */
		if (vm_readSingleBit(dev, CORE_USART_RCSTA, CORE_USART_BIT_SPEN) &&
			vm_readSingleBit(dev, CORE_USART_TXSTA, CORE_USART_BIT_TXEN))
		{
			/* clear TXIF interrupt */
			vm_writeSingleBit(dev, CORE_USART_PIR1, CORE_USART_BIT_TXIF, 0);
						
			/* fake serial buffer is full */
			vm_writeSingleBit(dev, CORE_USART_TXSTA, CORE_USART_BIT_TRMT, 0);			
		}	

		
	return 0;	
}


void vm_services_usart_rx(void *dev, char data)
{
	/* write the inbound character */
	vm_writeRam(dev, CORE_USART_RCREG, data);
	
	/* set the receive interrupt flag */
	vm_writeSingleBit(dev, CORE_USART_PIR1, CORE_USART_BIT_RCIF, 1);

	/* if interrupt is enabled, raise it */
	if (vm_readSingleBit(dev, CORE_USART_PIE1, CORE_USART_BIT_RCIE))
		vm_raiseInt(dev);
}